#define _CRT_SECURE_NO_WARNINGS 1

// 结构体
// 想描述一个学生，可以名字+年龄+成绩

//// 类型就是一个模具
//struct Stu
//{
//	char name[20];
//	int age;
//	float score;
//} s3, s4;
//// 这地方的s3,s4也是结构体变量，但是是全局变量
//
//int main()
//{
//	struct Stu s1 = {"ZhangSan", 20, 95.5f}; // 局部变量
//	struct Stu s2 = {"LiSi", 18, 87.5f};
//
//	struct Stu s5 = { .score = 98.5f };
//
//	return 0;
//}

//#include <stdio.h>
//struct Point
//{
//	int x;
//	int y;
//};
//
//struct Data
//{
//	int num;
//	struct Point p;
//};
//
//int main()
//{
//	struct Data d = { 200, {10, 11} }; // 嵌套初始化
//	printf("num = %d, p.x = %d, p.y = %d\n", d.num, d.p.x, d.p.y);
//
//	struct Data* pd = &d; // 取出s1的地址
//	// 结构体指针 -> 成员名
//	printf("%d %d", pd->num, pd->p.x);
//}

// 表达式求值
// 整型提升的意义：表达式的整型运算要在CPU的相应运算器件内执行
// CPU内整型运算器的操作数的字节长度一般就是int的字节长度

// char类型的取值范围是-128-127
// char是占用1个字节的，1个字节是8个bit位
// 如何进行整体提升？
// 1.有符号整数提升是按照变量的数据类型的符号位来提升的
// 2.无符号整数提升，高位补0

//#include <stdio.h>
//
//int main()
//{
//	char c1 = 125;
//	char c2 = 10;
//	char c3 = c1 + c2;
//
//	printf("%d", c3);
//
//	return 0;
//}

// 问题表达式
// a * b + c * d + e * f 用括号，不要直接这样子写
// c + --c --最优先，但是无法确定+操作符的左操作数的获取在右操作数之前还是之后求值
//#include <stdio.h>
//
//int fun()
//{
//	static int count = 1;
//	return ++count;
//}
//
//int main()
//{
//	int answer;
//	// 优先级只能决定先算谁，但先调用谁不一定
//	// 因此，可能是2 - 3 * 4 或者是 4 - 2 * 3
//	answer = fun() - fun() * fun();
//	printf("%d", answer);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int i = 1;
//	int ret = (++i) + (++i) + (++i);
//
//	printf("%d\n", ret);
//	printf("%d\n", i);
//
//	return 0;
//}

// 总结：不要写出特别复杂的表达式

// 1.内存被划分为一个个的单元，一个内存单元的大小就是一个字节
//#include <stdio.h>
//
//int main()
//{
//	// 四个字节里面，拿到的地址是低地址
//	int a = 10;
//	printf("%p\n", &a);
//
// 这里的星表示pa是指针变量，存放指针
//	int* pa = &a; // pa是指针变量，存放地址 地址有被称为指针，指针变量是用来存放指针的
//	
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	printf("%d\n", a);
//
//	int* pa = &a;
//	*pa = 20;
//	printf("%d\n", a);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//
//	// 指针变量是专门用来存放地址的
//	// 而指针变量的大小，取决于一个地址的存放需要多大空间
//	// 32位机器上，地址线是32根，地址的二进制序列就是32bit位
//	// 要存放这个地址，需要4个字节的空间，也就是32bit位的空间
//	// 因此，32位机器上指针变量的大小都是4个字节！
//
//	char* pc = NULL;
//	int* pi = NULL;
//
//	printf("%zd\n", sizeof(pc));
//	printf("%zd\n", sizeof(pi));
//
//	return 0;
//}

// 指针变量的大小和类型无关
// 只要是指针变量，在同一平台下，大小都是一样的
// 解引用的时候，不同指针可能会有区别

//#include <stdio.h>
//
//int main()
//{
//	int a = 0x11223344;
//	char* pa = &a;
//
//	*pa = 0; // 实际上只改动了一个字节
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	int* pa = &a;
//	char* pc = (char*)&a; // int*
//
//	printf("pa = %p\n", pa);
//	printf("pc = %p\n", pc);
//
//	printf("pa + 1 = %p\n", pa + 1);
//	printf("pc + 1 = %p\n", pc + 1);
//
//	return 0;
//}

// 结论：1.指针的类型决定了，对指针解引用的时候有多大的权限，即一次能操作几个字节
//      2.指针的类型决定了指针向前或者向后走一步有多大

//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 0 };
//	char* p = &arr[0];
//
// 一个一个字节来处理的
//	for (int i = 0; i < 40; i++) {
//		*p = 1;
//		p++;
//	}
//
//	p = &arr[0];
//
//	for (int i = 0; i < 40; i++) {
//		printf("%d ", *p);
//		p++;
//	}
//
//	return 0;
//}

// void*指针(泛型指针)
// 可以用来接受任意类型地址，但是也有局限性
// 不能直接进行指针的+-整数和解引用的运算

//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	char* ch = 'w';
//
//	int* pa = &a;
//	char* pc = &a; // warning
//
//	void* pv1 = &a; // int*
//	void* pv2 = &ch; // char*
//
//	*pv1 = 20; // void*指针不能直接进行解引用操作
//	pv1++; // void*指针不能直接进行指针的+-整数
//
//	return 0;
//}

// 一般void*类型的指针是使用在函数参数的部分，用于接受有不同类型数据的地址
// 一般用于泛型编程

// const修饰指针
//#include <stdio.h>
//
//int main()
//{
//	const int a = 10; // a不能被修改，但是a的本质还是变量，const仅仅是在语法上做了限制
//	a = 20; // 修改
//
//	printf("a = %d\n", a);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	const int a = 10;
//
//	// const可以用来修饰指针
//	const int* pa = &a;
//	/**pa = 0;*/
//
//	// 可以把a给改了，在pa不加const的情况下
//	printf("%d\n", a);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	// const修饰指针的时候
//	// const可以放在*的左边
//	// const可以放在*的右边
//
//	const int a = 10;
//	const int* p = &a;
//	// int const* p = &a; 作用同上
//
//	// const放在*号的左边，限制是*p
//	// 不能通过指针变量p修改p指向的空间的内容
//	// *p = 20; // err
//	// 但是p是不受限制的
//	// p = &b; // ok
//
//	// const放在*的右边，限制是p变量，也就是p变量不能被修改了
//	// 没办法再指向其他变量了
//	// 但是*p不受限制，还是可以通过p来修改p所指向的对象的内容
//	// p = &b; // err
//	// *p = 20; // ok
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int* pi = &arr[0];
//
//	for (int i = 0; i < 10; i++) {
//		printf("%d ", *pi);
//		pi++;
//	}
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	// 指针 - 指针 = 地址 - 地址
//  指针 - 指针有什么用呢
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//
//	// 指针 - 指针的绝对值得到的是指针和指针之间的元素个数
//	printf("%d\n", &arr[9] - &arr[0]);
//	// 指针 - 指针的前提条件是：两个指针指向同一空间
//	
//	return 0;
//}
// 指针 - 指针就像 日期 - 日期
// 日期 - 日期 = 中间的天数
// 日期 + 天数 = 日期
// 日期 - 天数 = 日期

#include <stdio.h>
#include <string.h>

// 模拟实现strlen函数
//int my_strlen(char* str)
//{
//	int count = 0;
//	while (*str != '\0') {
//		count++;
//		str++;
//	}
//
//	return count;
//}

//int my_strlen(char* str)
//{
//	char* start = str;
//	while (*str != '\0') {
//		str++;
//	}
//
//	return str - start;
//}
//
//int main()
//{
//	// 求字符串的长度-统计的是\0前面出现的字符的个数
//	int len = my_strlen("abcdef");
//	printf("%d\n", len);
//
//	return 0;
//}

//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//
//	// 使用while循环打印数组arr的内容
//	// arr是数组名，数组名其实是数组首元素的地址
//	int* pa = &arr[0];
//	while (pa < arr + sz) {
//		printf("%d ", *pa);
//		pa++;
//	}
//
//	return 0;
//}

// 野指针指的是指针指向的位置是不可知的
//#include <stdio.h>
//
//int* test()
//{
//	int a = 10;
//
//	return &a;
//}
//
//int main()
//{
//	int* p = test();
//	// 空间释放了
//	printf("hehe\n"); // 为甚恶魔这里加了一句代码，*p的值就变了？
//	printf("%d\n", *p); 
//
//	return 0;
//}
// 不要返回局部变量的地址
// 指针变量不再使用时候，及时置NULL，使用前要检查有效性
































